﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Windows;

using CAION2.Classes;
using CAION2.Helpers;
using CAION2.Commands;
using CAION2.Properties;

namespace CAION2.ViewModels
{
    using System.Globalization;
    using System.IO;
    using System.Xml.Linq;

    using Models;

    using FirstFloor.ModernUI.Windows.Controls;

    class StatusViewModel : ViewModelBase
    {
        #region Vars

        private List<string> _gameClasses;
        private string _characterClass;
        private byte _characterLevel;

        private List<ItemPlaceViewModel> _places;
        private CharacterStats _stats;
        private DataView _statsView;
        private ItemSets _itemSets;
        
        private Byte _maxCharacterLevel;

        private ItemCardViewModel _currentCard;
        private string _currentSlotName;
        private Visibility _placesVisible;
        private List<bool> _statsListColumnsVisible;
        
        #endregion
        
        #region Properties

        private ItemSets ItemSets
        {
            get { return _itemSets; }
            set
            {
                if (Equals(_itemSets, value)) return;
                _itemSets = value;
            }
        }

        private CharacterStats Stats
        {
            get { return _stats; }
            set
            {
                if (Equals(_stats, value)) return;
                _stats = value;

                StatsView = Stats.StatsTable.DefaultView;
                StatsView.ApplyDefaultSort = true;
            }
        }

        public List<ItemPlaceViewModel> Places
        {
            get { return _places; }
            private set
            {
                if (Equals(_places, value)) return;
                _places = value;
            }
        }

        public ItemCardViewModel CurrentCard
        {
            get { return _currentCard; }

            private set
            {
                _currentCard = value;
                OnPropertyChanged("CurrentCard");

                if (!CurrentCard.CardIsLoaded) CurrentCard.LoadCard();
            }
        }
        
        public string CurrentSlotName
        {
            get { return _currentSlotName; }
            private set
            {
                if (Equals(_currentSlotName, value)) return;
                _currentSlotName = value;

                OnPropertyChanged("CurrentSlotName");
            }
        }

        public string CurrentCharacterClass
        {
            get { return _characterClass; }
            private set
            {
                if (Equals(_characterClass, value)) return;
                _characterClass = value;

                OnPropertyChanged("CurrentCharacterClass");

                SetGameClassProperties();
                StatusIsChange(CurrentCharacterLevel, value);
                
                Mediator.Instance.NotifyColleagues(Messages.StatsGameClassSend, CurrentCharacterClass);
                if (!SaveCharacterCommand.IsEnabled) SaveCharacterCommand.IsEnabled = true;
            }
        }
        
        public Visibility PlacesVisible
        {
            get { return _placesVisible; }
            private set
            {
                if (Equals(_placesVisible, value)) return;
                _placesVisible = value;

                OnPropertyChanged("PlacesVisible");
            }
        }
        
        public byte CurrentCharacterLevel
        {
            get { return _characterLevel; }
            set
            {
                if (Equals(_characterLevel, value)) return;
                _characterLevel = value;

                OnPropertyChanged("CurrentCharacterLevel");

                StatusIsChange(value, CurrentCharacterClass);
                Mediator.Instance.NotifyColleagues(Messages.StatsCharacterLevelSend, CurrentCharacterLevel);
            }
        }

        public DataView StatsView
        {
            get { return _statsView; }
            private set
            {
                if (Equals(_statsView, value)) return;
                _statsView = value;

                OnPropertyChanged("StatsView");
            }
        }
        
        public List<bool> StatsListColumnsVisible
        {
            get { return _statsListColumnsVisible; }
            private set
            {
                if (Equals(_statsListColumnsVisible, value)) return;
                _statsListColumnsVisible = value;

                OnPropertyChanged("StatsListColumnsVisible");
            }
        }

        public List<string> GameClasses
        {
            get { return _gameClasses; }
            private set
            {
                if (Equals(_gameClasses, value)) return;
                _gameClasses = value;

                OnPropertyChanged("GameClasses");
            }
        }

        public byte MaxCharacterLevel
        {
            get { return _maxCharacterLevel; }
            private set
            {
                if (Equals(_maxCharacterLevel, value)) return;
                _maxCharacterLevel = value;

                OnPropertyChanged("MaxCharacterLevel");
            }
        }

        #endregion

        #region Commands

        public RelayCommand SaveCharacterCommand { get; private set; }
        public RelayCommand LoadCharacterCommand { get; private set; }
        public RelayCommand ClearPlacesCommand { get; private set; }
        public RelayCommand<string> SelectStatsListCommand { get; private set; }
        public RelayCommand StatsListColumnVisibleCommand { get; private set; }

        #endregion

        public StatusViewModel()
        {
            WireCommands();

            Stats = new CharacterStats();
            ItemSets = new ItemSets();

            MaxCharacterLevel = ClassConfiguration.CharacterMaximumLevel;
            CurrentCharacterLevel = MaxCharacterLevel;
            
            CurrentCard = new ItemCardViewModel();
            PlacesVisible = Visibility.Hidden;
            SelectStatsList("all");

            Mediator.Instance.Register(o => ShowCard((ItemPlaceViewModel)o), Messages.ShowCard);
            Mediator.Instance.Register(o => CheckItemSet((string[])o, true), Messages.ItemSetAddItem);
            Mediator.Instance.Register(o => CheckItemSet((string[])o, false), Messages.ItemSetDelItem);

            Places = new List<ItemPlaceViewModel>
                {
                    new ItemPlaceViewModel(1, ItemPlaceViewModel.EquipmentSlots.Main),
                    new ItemPlaceViewModel(2, ItemPlaceViewModel.EquipmentSlots.Sub),
                    new ItemPlaceViewModel(3, ItemPlaceViewModel.EquipmentSlots.Wing),
                    new ItemPlaceViewModel(4, ItemPlaceViewModel.EquipmentSlots.Head),
                    new ItemPlaceViewModel(5, ItemPlaceViewModel.EquipmentSlots.RightOrLeftEar, false),
                    new ItemPlaceViewModel(6, ItemPlaceViewModel.EquipmentSlots.Torso),
                    new ItemPlaceViewModel(7, ItemPlaceViewModel.EquipmentSlots.Leg),
                    new ItemPlaceViewModel(8, ItemPlaceViewModel.EquipmentSlots.RightOrLeftFinger, false),
                    new ItemPlaceViewModel(9, ItemPlaceViewModel.EquipmentSlots.Foot),
                    new ItemPlaceViewModel(10, ItemPlaceViewModel.EquipmentSlots.Neck),
                    new ItemPlaceViewModel(11, ItemPlaceViewModel.EquipmentSlots.RightOrLeftEar),
                    new ItemPlaceViewModel(12, ItemPlaceViewModel.EquipmentSlots.Shoulder),
                    new ItemPlaceViewModel(13, ItemPlaceViewModel.EquipmentSlots.Glove),
                    new ItemPlaceViewModel(14, ItemPlaceViewModel.EquipmentSlots.RightOrLeftFinger),
                    new ItemPlaceViewModel(15, ItemPlaceViewModel.EquipmentSlots.Waist),
                    new ItemPlaceViewModel(16, ItemPlaceViewModel.EquipmentSlots.Armsfused)
                };

            GameClasses = new List<string>
                {
                    Resources.Class_Fighter,
                    Resources.Class_Knight,
                    Resources.Class_Assassin,
                    Resources.Class_Ranger,
                    Resources.Class_Wizard,
                    Resources.Class_Elementalist,
                    Resources.Class_Priest,
                    Resources.Class_Chanter,
                    Resources.Class_Bard,
                    Resources.Class_Gunner,
                    Resources.Class_Rider
                };

            StatsListColumnsVisible = ClassConfiguration.GetBoolGroup("StatsListColumnVisible").ToList();
        }

        private void WireCommands()
        {
            SaveCharacterCommand = new RelayCommand(SaveCharacter) { IsEnabled = false };
            LoadCharacterCommand = new RelayCommand(LoadCharacter) { IsEnabled = true };
            ClearPlacesCommand = new RelayCommand(ClearPlaces) { IsEnabled = true };
            SelectStatsListCommand = new RelayCommand<string>(SelectStatsList) { IsEnabled = true };
            StatsListColumnVisibleCommand = new RelayCommand(TurnStatsListColumnVisible) { IsEnabled = true };
        }

        private void ShowCard(ItemPlaceViewModel itemPlace)
        {
            CurrentCard = itemPlace.Card;
            CurrentSlotName = itemPlace.SlotName;
        }

        private void SetGameClassProperties()
        {
            PlacesVisible = String.IsNullOrEmpty(CurrentCharacterClass) ? Visibility.Hidden : Visibility.Visible;
            Places[1].IsEnabled = ClassGameClass.GetEnableForSubSlotPlace(CurrentCharacterClass);
        }

        private void StatusIsChange(byte newLevel, string newClass)
        {
            if (Places == null || string.IsNullOrEmpty(newClass)) return;

            foreach (ItemPlaceViewModel place in Places.Where(place => place.Card.Item != null))
            {
                place.SetRedBackground(newLevel, newClass);
            }

            Stats.FillCharacterStats(newClass, newLevel);
            Stats.LoadPassiveSkills();
            Stats.CalculateStats();
        }
        
        private void ClearPlaces()
        {
            foreach (ItemPlaceViewModel place in Places)
            {
                place.DeletePlace();
            }

            ItemPlaceViewModel wItemPlace = Places.Find(p => p.Index == 1);
            ShowCard(wItemPlace);
        }

        private void SaveCharacter()
        {
            string xfile = ClassFile.ShowSaveFileDialog(ClassGameClass.GetGameClassNameForFileName(CurrentCharacterClass) + ".xml",
                                            ".xml", "XML (.xml)|*.xml", DataModel.WorkDirectory + "\\Profiles\\");
            if (xfile == "") return;

            UiServices.SetBusyState();

            XDocument xSave = new XDocument();
            XElement xRoot = new XElement("Profile");
            xSave.Add(xRoot);

            xRoot.Add(new XAttribute("Version", "3.0"));
            xRoot.Add(new XAttribute("GameClass", ClassGameClass.GetGameClassNameForProfileFile(CurrentCharacterClass)));
            xRoot.Add(new XAttribute("Level", CurrentCharacterLevel.ToString(CultureInfo.InvariantCulture)));

            foreach (ItemPlaceViewModel place in Places)
            {
                xRoot.Add(place.Card.GetCardElementForSaveToFile());
            }

            xSave.Save(xfile);
        }
        
        private void LoadCharacter()
        {
            string xfile = ClassFile.ShowOpenFileDialog("", ".xml", "XML file (.xml)|*.xml",
                                            DataModel.WorkDirectory + "\\Profiles\\");
            if (xfile == "") return;

            UiServices.SetBusyState();

            try
            {
                LoadProfileFromFile(xfile);
            }
            catch (Exception err)
            {
                ModernDialog md = new ModernDialog { Content = string.Format("{0}", err.Message) };
                md.ShowDialog();
                File.WriteAllText(@"error_file.txt", err.StackTrace);
            }
        }
        
        private void LoadProfileFromFile(string pathFile)
        {
            UiServices.SetBusyState();

            if (!File.Exists(pathFile)) throw new FileNotFoundException(string.Format("Файл не найден (File not found): {0}", pathFile));

            XDocument xLoad = XDocument.Load(pathFile);
            XElement xRoot = xLoad.Element("Profile");
            if (xRoot == null) throw new Exception("Неподдерживаемая версия файла (Wrong file-format version)!");

            XAttribute xVersion = xRoot.Attribute("Version");
            if (xVersion == null || xVersion.Value != "3.0") throw new Exception("Неподдерживаемая версия файла (Wrong file-format version)!");

            Stats.FreezeCalculate = true;
            ClearPlaces();
            _itemSets = new ItemSets();

            CurrentCharacterClass = ClassGameClass.GetGameClassNameFromProfileFile(xRoot.Attribute("GameClass").Value);
            CurrentCharacterLevel = Convert.ToByte(xRoot.Attribute("Level").Value);

            foreach (XElement xPlace in xRoot.Elements("Place").Where(xPlace => xPlace.Attribute("Item") != null))
            {
                ItemPlaceViewModel itemPlace =
                    Places.Find(p => p.Index == Convert.ToByte(xPlace.Attribute("Index").Value));

                DataRow row = DataModel.GetRowAtName(DataModel.GetTableNameFromSlot(itemPlace.Slot), xPlace.Attribute("Item").Value);

                itemPlace.Card.PlaceIndex = itemPlace.Index;
                if (itemPlace.Index == 16)
                {
                    itemPlace.Card.IsArmsfusionItem = true;
                }
                itemPlace.Card.LoadItem(row);
                itemPlace.Card.LoadCard();

                if (xPlace.Attribute("Enchant") != null)
                    itemPlace.Card.Enchant = Convert.ToByte(xPlace.Attribute("Enchant").Value);

                if (xPlace.Attribute("Idian") != null)
                    itemPlace.Card.LoadIdian(xPlace.Attribute("Idian").Value);

                if (xPlace.Attribute("RandomBonus") != null)
                    itemPlace.Card.LoadRandomBonus(xPlace.Attribute("RandomBonus").Value);

                itemPlace.Card.SetGodEnchantLevel(
                    xPlace.Attribute("GodEnchant") != null ? xPlace.Attribute("GodEnchant").Value : "0");

                if (xPlace.Elements("Manastone").Any())
                {
                    foreach (XElement xStone in xPlace.Elements("Manastone").Where(xStone => xStone.Attribute("Name") != null))
                    {
                        itemPlace.Card.LoadManastone((byte)(Convert.ToByte(xStone.Attribute("Index").Value) - 1), xStone.Attribute("Name").Value);
                    }
                }

                itemPlace.Update();

                string[] itemInfo = { itemPlace.Card.Item["name"].ToString(), itemPlace.Card.Item.Table.TableName };
                CheckItemSet(itemInfo, true);
            }

            ItemPlaceViewModel wItemPlace = Places.Find(p => p.Index == 1);
            wItemPlace.GetMainSlotWeaponType();
            ItemPlaceViewModel afItemPlace = Places.Find(p => p.Index == 16);
            wItemPlace.SetArmsfusionInMain(afItemPlace.Card);

            Stats.FreezeCalculate = false;
            StatusIsChange(CurrentCharacterLevel, CurrentCharacterClass);
        }

        private void SelectStatsList(string index)
        {
            switch (index)
            {
                case "all":
                    StatsView.RowFilter = "BasicSum <> 0 OR BonusSum <> 0";
                    break;
                case "hands":
                    StatsView.RowFilter = "Name IN ('phyattack', 'hitaccuracy', 'critical', 'phyattack_l', 'hitaccuracy_l', 'critical_l')";
                    break;
                case "basics":
                    StatsView.RowFilter = "(BasicSum <> 0 OR BonusSum <> 0) AND Name NOT IN ('phyattack', 'hitaccuracy', 'critical')";
                    break;
            }
        }

        private void TurnStatsListColumnVisible()
        {
            OnPropertyChanged("StatsListColumnsVisible");
            ClassConfiguration.SetBoolGroup("StatsListColumnVisible", StatsListColumnsVisible);
        }

        private void CheckItemSet(string[] itemInfo, bool isAddItem)
        {
            if (isAddItem)
            {
                ItemSets.AddItemSet(itemInfo);
            }
            else
            {
                ItemSets.DelItemSet(itemInfo[0]);
            }

            Mediator.Instance.NotifyColleagues(Messages.ItemSetSend, ItemSets);

            Stats.ClearStatsFromItemSets();
            foreach (ItemSet itemSet in ItemSets.ItemsSetList)
            {
                Stats.FillStatsFromItemSet(itemSet.StatsList);
            }
            Stats.CalculateStats();
        }

    }
}
